///////////////////////////////////////////////////////////////////////////////
//
// Copyright
//
///////////////////////////////////////////////////////////////////////////////

package ru.goodcore.resource
{

import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.utils.ByteArray;
import flash.utils.getClassName;

import ru.goodcore.errors.GenericErrorCode;
import ru.goodcore.logging.Log;
import ru.goodcore.net.URI;
import ru.goodcore.resource.errors.ResourceBundleError;
import ru.goodcore.utils.crypto.MD5;
import ru.goodcore.utils.gif.GIFDecoder;
import ru.goodcore.utils.swf.SWFBitmapData;
import ru.goodcore.display.SWFGIFMovieClip;
import ru.goodcore.utils.swf.SWFImage;
import ru.goodcore.utils.swf.SWFMovieClip;

/**
 * 
 * @author					Dimarik
 * @version					1.0
 * @playerversion			Flash 9
 * @langversion				3.0
 *
 * @keyword					
 */
public class ResourceManager
{

	include "../../../includes/core/Version.as";

	//-------------------------------------------------------------------------
	//
	//  Class variables
	//
	//-------------------------------------------------------------------------

	/**
	 * @private
	 * Содержит ссылки на ResourceVault.
	 * 
	 * ключ - сигнатура конкретного ресурса,
	 * значение - ResourceVault, в котором находится требеумый ресурс
	 */	
	private static var _resourceVaultsStorage:	Object = new Object();

	/**
	 * @private
	 * Содержит url набора ресурсов, в который входит искомый
	 * 
	 * ключ - сигнатура конкретного ресурса,
	 * значение - url набора ресурсов
	 */
	private static var _resourceMap:			ResourceMap = new ResourceMap();


	/**
	 * @private
	 */
	private static var _mapUrlToRV:				Object = new Object();

	//-------------------------------------------------------------------------
	//
	//  Constructor
	//
	//-------------------------------------------------------------------------

	/**
	 * Constructor
	 */
	public function ResourceManager() {
		super();
	}

	//-------------------------------------------------------------------------
	//
	//  Properties
	//
	//-------------------------------------------------------------------------

	/**
	 * Stage FPS
	 */	
	public var fps:	uint = 0;

	//-------------------------------------
	//  throwOnNotFound
	//-------------------------------------
	/**
	 * @private
	 */
	private var _throwOnNotFound:	Boolean = false;

	/**
	 * @return <code>true</code> if throw allowed
	 */	
	public function get throwOnNotFound():Boolean {
		return this._throwOnNotFound;
	}

	public function set throwOnNotFound(value:Boolean):void {
		if (this._throwOnNotFound == value) return;
		this._throwOnNotFound = value;
	}

	//-------------------------------------
	//  warnOnReplace
	//-------------------------------------
	/**
	 * @private
	 */
	private var _warnOnReplace:	Boolean = true;

	/**
	 * @return <code>true</code> if throw allowed
	 */	
	public function get warnOnReplace():Boolean {
		return this._warnOnReplace;
	}

	public function set warnOnReplace(value:Boolean):void {
		if (this._warnOnReplace == value) return;
		this._warnOnReplace = value;
	}

	//-------------------------------------------------------------------------
	//
	//  Private variables
	//
	//-------------------------------------------------------------------------
	
	/**
	 * @private
	 */
	private var _qname:				QName;

	/**
	 * @private
	 */
	private var _resourceSignature:	String;

	//-------------------------------------------------------------------------
	//
	//  Public methods
	//
	//-------------------------------------------------------------------------

	/**
	 * 
	 * @param value
	 * @return 
	 * 
	 */	
	public function load(bundle:ResourceBundle):void {
		
	}



	/**
	 * 
	 * @param value
	 * @return 
	 * 
	 */	
	public function getAccessNamespace(value:Object):Namespace {
		return null;
	}

	/**
	 * 
	 */
	public function addResourceEntry(uri:URI):void {
		var resourceSignature:String = MD5.hash(uri.toDisplayString());

		if (resourceSignature in _resourceMap && this._warnOnReplace)
			Log.getLogger(this).warn('Resource ' + resourceSignature + ' already exist');

		_resourceMap[resourceSignature] = uri;
	}


	/**
	 * 
	 */
	public function getResourceVault(resourceSignature:String):ResourceVault {
		this._resourceSignature = resourceSignature;
		var resourceVault:	ResourceVault = null;
		var url:			String = _resourceMap[resourceSignature];


		if ( !url ) {
			if (this._throwOnNotFound)
				throw new ResourceBundleError(ResourceBundleError.MISSING_BUNDLE);
			else 
				return null;
		}


		// Ищем подходящий ResourceVault по сигнатуре ресурса
		if (resourceSignature in _resourceVaultsStorage) {
			resourceVault = ResourceVault(_resourceVaultsStorage[resourceSignature]);

			// resourceVault не готов, необходима загрузка в него контента
			if ( !resourceVault.ready ) {
				// Add to loader queue

				// this._loadingQueue.addResourceVault
			}

			return resourceVault;
		}








		if (resourceSignature in _resourceMap) {

			if (url in _mapUrlToRV) {
				resourceVault = _mapUrlToRV[url];
				return resourceVault;
			}

			else {
				
				// Define QName
				var uri:	URI = new URI(url);
				var path:	String = uri.path;

				path = path.replace(/[\\\/]/g, '.');
				path = path.replace(/\|/g, '$');
				path = path.replace(/^\./g, '');

				var index:int = path.lastIndexOf('.');

				var url1:		String = '';
				var localName:	String;
				if (index >= 0) {
					url1 = path.substr(0, index);
					localName = path.substr(index + 1);
				} else {
					localName = path;
				}

				this._qname = new QName(url1, localName);

				var request:	URLRequest = new URLRequest(url);
				var loader:		Loader = new Loader();
				var appDomain:	ApplicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
				var context:	LoaderContext = new LoaderContext(true,  appDomain);

				resourceVault = new ResourceVault();
				_resourceVaultsStorage[resourceSignature] = resourceVault;
				_mapUrlToRV[url] = resourceVault;

				var urlLoader:URLLoader = new URLLoader();
				urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
				Log.getLogger(this).info('Load resource %s0', request.url);
				if ( url.toLowerCase().match(/\.png|jpg|jpeg/) ) {
					//loader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.handler_contentComplete);
					//loader.load(request, context);

					urlLoader.addEventListener(Event.COMPLETE, this.handler_contentComplete);
					urlLoader.load(request);
				} else if ( url.toLowerCase().match(/\.gif/) ) {
					urlLoader.addEventListener(Event.COMPLETE, this.handler_gifComplete);
					urlLoader.load(request);
				} else {
					resourceVault.appDomain = appDomain;
					resourceVault.dispatcher = loader.contentLoaderInfo;
					loader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.handler_resourceComplete, false, int.MAX_VALUE);
					loader.load(request, context);
				}

				
			}


		}

		return resourceVault;
	}

	/**
	 * 
	 */
	public function getResource(bundleName:String, resourceName:String, 
								locale:String = null):Resource
	{
		return new Resource();
	}







	/**
	 * 
	 */
	public function getString(bundleName:String, resourceName:String,
								replaces:Array = null,
								locale:String = null):String
	{
		var resourceBundle:ResourceBundle = this.findBundle(bundleName, resourceName, locale);

		if ( !resourceBundle ) {
			if (this._throwOnNotFound)
				throw new ResourceBundleError(ResourceBundleError.MISSING_BUNDLE);

			return null;
		}

		var value:String = resourceBundle.getString(resourceName);

		if (replaces) {
			for (var i:uint = 0; i < replaces.length; i++)
				value = value.replace(new RegExp('\\{' + i + '\\}', 'g'), replaces[i]);
		}

		return value;
	}

	//-------------------------------------------------------------------------
	//
	//  Protected methods
	//
	//-------------------------------------------------------------------------

	//-------------------------------------------------------------------------
	//
	//  Private methods
	//
	//-------------------------------------------------------------------------
	/**
	 *  @private.
	 */
	private function findBundle(bundleName:String, resourceName:String,
								locale:String):ResourceBundle
	{

		var resourceBundle:ResourceBundle = new ResourceBundle('GenericError');

		// TODO DEBUG
		resourceBundle.addResource(String(GenericErrorCode.FOREIGN_EVENT_DISPATCH), 
						'%className попытался распространить постороннее событие {0}.');

		resourceBundle.addResource(String(GenericErrorCode.TYPE_COERCION_FAILED), 
						'Не удалось осуществить приведение к типу: не удалось преобразовать %s0 в %s1.');

		return resourceBundle;

	}

	//-------------------------------------------------------------------------
	//
	//  Event handlers
	//
	//-------------------------------------------------------------------------

	/**
	 * @private
	 *  
	 * Invoked when GIF content loaded
	 * 
	 * @param event
	 * 
	 */
	private function handler_gifComplete(event:Event):void {
		Log.getLogger(this).info('Resource loaded. Decoding...');
		var urlLoader:URLLoader = URLLoader(event.target);
		urlLoader.removeEventListener(Event.COMPLETE, this.handler_gifComplete);
		this.createAndLoadGIFMovieClip(urlLoader.data, this._qname);
	}

	/**
	 * @private
	 *  
	 * Invoked when GIF content loaded
	 * 
	 * @param event
	 * 
	 */
	private function handler_gifComplete1(event:Event):void {
		Log.getLogger(this).info('Resource loaded. Decoding...');
		var urlLoader:URLLoader = URLLoader(event.target);
		urlLoader.removeEventListener(Event.COMPLETE, this.handler_gifComplete1);
		var arrayOfBitmaps:Array = GIFDecoder.decode(urlLoader.data);
		this.createAndLoadMovieClip(arrayOfBitmaps, this._qname);
	}

	/**
	 * @private
	 *  
	 * Invoked when content loaded
	 * 
	 * @param event
	 * 
	 */	
	private function handler_contentComplete(event:Event):void {
		var urlLoader:URLLoader = URLLoader(event.target);
		urlLoader.removeEventListener(Event.COMPLETE, this.handler_contentComplete);

		//trace(traceByteArray(loaderInfo.bytes));
		//trace(getDefinitionNames(loaderInfo.bytes));

		//var sourceBitmapData:BitmapData = Bitmap(loader.content).bitmapData;

		//this.createAndLoadSWF(sourceBitmapData, this._qname);
		
		this.createAndLoadJPEG(ByteArray(urlLoader.data), this._qname);
	}

	/**
	 * @private
	 *  
	 * Invoked when content loaded
	 * 
	 * @param event
	 * 
	 */	
	private function handler_contentComplete1(event:Event):void {
		var loaderInfo:LoaderInfo = LoaderInfo(event.target);
		loaderInfo.removeEventListener(Event.COMPLETE, this.handler_contentComplete);

		var loader:Loader = loaderInfo.loader;

		//trace(traceByteArray(loaderInfo.bytes));
		//trace(getDefinitionNames(loaderInfo.bytes));

		//var sourceBitmapData:BitmapData = Bitmap(loader.content).bitmapData;

		//this.createAndLoadSWF(sourceBitmapData, this._qname);

		//this.createAndLoadJPEG(ByteArray(urlLoader.data), this._qname);
	}

	private function createAndLoadGIFMovieClip(input:ByteArray, qname:QName):void {
		Log.getLogger(this).info('createAndLoadGIFMovieClip');
		var swfGIFMovieClip:SWFGIFMovieClip = new SWFGIFMovieClip(input, qname, true, this.fps);

		//trace(traceByteArray(swfImage));

		var resourceVault:ResourceVault = ResourceVault( _resourceVaultsStorage[this._resourceSignature] );

		var appDomain:	ApplicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
		var context:	LoaderContext = new LoaderContext(false,  appDomain);

		var loader:Loader = new Loader();
		var loaderInfo:LoaderInfo = loader.contentLoaderInfo;

		resourceVault.dispatcher = loaderInfo;

		resourceVault.appDomain = appDomain;		

		loaderInfo.addEventListener(Event.COMPLETE, this.handler_resourceComplete, false, int.MAX_VALUE);
		loader.loadBytes( swfGIFMovieClip, context );
	}

	private function createAndLoadMovieClip(arr:Array, qname:QName):void {
		Log.getLogger(this).info('CreateAndLoadMovieClip');
		var swfImage:SWFMovieClip = new SWFMovieClip(arr, qname, true);

		//trace(traceByteArray(swfImage));

		var resourceVault:ResourceVault = ResourceVault( _resourceVaultsStorage[this._resourceSignature] );

		var appDomain:	ApplicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
		var context:	LoaderContext = new LoaderContext(false,  appDomain);

		var loader:Loader = new Loader();
		var loaderInfo:LoaderInfo = loader.contentLoaderInfo;

		resourceVault.dispatcher = loaderInfo;

		resourceVault.appDomain = appDomain;		

		loaderInfo.addEventListener(Event.COMPLETE, this.handler_resourceComplete, false, int.MAX_VALUE);
		loader.loadBytes( swfImage, context );
	}

	private function createAndLoadJPEG(ba:ByteArray, qname:QName):void {
		var swfImage:SWFImage = new SWFImage(ba, qname);
		
		//trace(traceByteArray(swfImage));
		
		var resourceVault:ResourceVault = ResourceVault( _resourceVaultsStorage[this._resourceSignature] );

		var appDomain:	ApplicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
		var context:	LoaderContext = new LoaderContext(false,  appDomain);
		
		var loader:Loader = new Loader();
		var loaderInfo:LoaderInfo = loader.contentLoaderInfo;

		resourceVault.dispatcher = loaderInfo;

		resourceVault.appDomain = appDomain;		

		//loaderInfo.addEventListener(Event.COMPLETE, this.handler_resourceComplete, false, int.MAX_VALUE);
		loader.loadBytes( swfImage, context );
	}

	private function createAndLoadSWF(bitmapData:BitmapData, qname:QName):void {
		// Construct SWF structure
		var swfBitmapData:SWFBitmapData = new SWFBitmapData(bitmapData, qname);

		var resourceVault:ResourceVault = ResourceVault( _resourceVaultsStorage[this._resourceSignature] );

		var appDomain:	ApplicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
		var context:	LoaderContext = new LoaderContext(false,  appDomain);

		var loader:Loader = new Loader();
		var loaderInfo:LoaderInfo = loader.contentLoaderInfo;

		resourceVault.dispatcher = loaderInfo;

		resourceVault.appDomain = appDomain;		

		//loaderInfo.addEventListener(Event.COMPLETE, this.handler_resourceComplete, false, int.MAX_VALUE);
		loader.loadBytes( swfBitmapData, context );
	}

	/**
	 * @private
	 *  
	 * Invoked when resource loaded
	 * 
	 * @param event
	 * 
	 */
	private function handler_resourceComplete(event:Event):void {
		trace(getClassName(this) + '; handler_resourceComplete: ' + event);
		Log.getLogger(this).info('ResourceComplete');
	}

}
}